<script lang="tsx">
import { toRefs, unref, computed } from 'vue';

import type { FormActionType, FormProps, FormSchema } from '../types/form';
import type { ValidationRule } from '../types/formItem';

import { isBoolean, isFunction, isNull } from 'lodash-es';
import { getSlot } from '../utils/helper/tsxHelper';
import { createPlaceholderMessage, setComponentRuleType } from '../helper';
import { cloneDeep, upperFirst } from 'lodash-es';
import { useItemLabelWidth } from '../hooks/useLabelWidth';
import { componentMap } from '../componentMap';
import { getCompDefWidth } from '../utils/utils';

import BasicHelp from './BasicHelp.vue';
import { QuestionCircleOutlined } from '@ant-design/icons-vue';

import { Col, Divider, Form } from 'ant-design-vue';

export default defineComponent({
    name: 'FormItem',
    inheritAttrs: false, // 不继承父类传递的默认参数
    props: {
        // 表单配置规则属性对象
        formProps: {
            type: Object as PropType<FormProps>,
            default: () => ({}),
        },
        // 表单（每项）配置属性对象
        schema: { 
            type: Object as PropType<FormSchema>,
            default: () => ({}),
        },
        // 表单绑定值模板对象
        bindModel: {
            type: Object as PropType<Recordable>,
            default: () => ({}),
        },
        // 表单项默认值数据
        allDefaultValues: {
            type: Object as PropType<Recordable>,
            default: () => ({}),
        },
        // 表单操作方法
        formActionMethod: {
            type: Object as PropType<FormActionType>,
        },

        // 表单项change触发事件
        itemChange: {
            type: Function as PropType<(key: string, value: any) => void>,
            default: null,
        },
    },

    setup(props, { slots }) {
        // 获取表单每项配置数据 和 表单总的基本配置数据
        const { schema, formProps } = toRefs(props) as { schema: Ref<FormSchema>; formProps: Ref<FormProps>; };

        const itemLabelWidthProp = useItemLabelWidth(schema, formProps); // 处理并获取对应的label宽度

        // 获取事件抛出的记录数据 如：render()、 show()、 ifShow()、 dynamicDisabled()、renderComponentContent() 等事件
        const getRecordData = computed(() => {
            const { schema, formProps, bindModel, allDefaultValues } =  props;
            return {
                schema: schema,
                field: schema['field'],
                model: bindModel,
                values: { ...formProps['mergeDynamicData'], ...allDefaultValues, ...bindModel } as Recordable,
            };
        });

        // 获取每项组件的props参数；
        const getComponentsProps = computed(() => {
            const { schema, bindModel, formActionMethod }:any = props;
            let { componentProps = {} } = schema;
            if (isFunction(componentProps)) {
                componentProps = componentProps({ schema, bindModel, formActionMethod }) ?? {};
            }
            if (schema.component === 'Divider') {
                componentProps = Object.assign({ type: 'horizontal' }, {
                    orientation: 'left',
                    plain: true,
                }, componentProps);
            }

            // 处理需要默认设置宽度的组件
            const setDefWidthObj = getCompDefWidth(schema.component); // 获取默认设置配置
            componentProps = Object.assign(componentProps, {
                style: componentProps.style ? Object.assign({}, setDefWidthObj, componentProps.style) : setDefWidthObj,
            });
            console.log('触发', componentProps);

            return componentProps as Recordable;
        });

        // 处理每项是否禁用： 如全局设置了globDisabled禁用；将有先取值globDisabled值进行禁用
        const getDisable = computed(() => {
            const { disabled: globDisabled } = props.formProps;
            const { dynamicDisabled } = props.schema;
            const { disabled: itemDisabled = false } = unref(getComponentsProps);
            let disabled = !!globDisabled || itemDisabled;
            if (isBoolean(dynamicDisabled)) { disabled = dynamicDisabled; }
            if (isFunction(dynamicDisabled)) { disabled = dynamicDisabled(unref(getRecordData)); }

            return disabled;
        });

        // 处理是否显示对应表单项
        function getShow(): { isShow: boolean; isIfShow: boolean } {
            const { show, ifShow } = props.schema;
            let isShow = true, isIfShow = true;

            if (isBoolean(show)) { isShow = show; }
            if (isBoolean(ifShow)) { isIfShow = ifShow; }
            if (isFunction(show)) { isShow = show(unref(getRecordData)); }
            if (isFunction(ifShow)) { isIfShow = ifShow(unref(getRecordData)); }

            return { isShow, isIfShow };
        }

        // 处理表单项校验内容逻辑
        function handleRules(): ValidationRule[] {
            const {
                rules: defRules = [],
                component,
                rulesMessageJoinLabel,
                label,
                dynamicRules,
                required,
            }:any = props.schema;

            if (isFunction(dynamicRules)) {
                return dynamicRules(unref(getRecordData)) as ValidationRule[];
            }

            let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];

            const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;

            const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') ? rulesMessageJoinLabel : globalRulesMessageJoinLabel;
            
            const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;

            // 定义校验方法
            function validator(rule: any, value: any) {
                const msg = rule.message || defaultMsg;
                if (value === undefined || isNull(value)) {
                    // 空值
                    return Promise.reject(msg);
                } else if (Array.isArray(value) && value.length === 0) {
                    // 数组类型
                    return Promise.reject(msg);
                } else if (typeof value === 'string' && value.trim() === '') {
                    // 空字符串
                    return Promise.reject(msg);
                } else if (
                    typeof value === 'object' &&
                    Reflect.has(value, 'checked') &&
                    Reflect.has(value, 'halfChecked') &&
                    Array.isArray(value.checked) &&
                    Array.isArray(value.halfChecked) &&
                    value.checked.length === 0 &&
                    value.halfChecked.length === 0
                ) {
                    // 非关联选择的tree组件
                    return Promise.reject(msg);
                }
                return Promise.resolve();
            }

            const getRequired = isFunction(required) ? required(unref(getRecordData)) : required;

           /*
            * 1、若设置了required属性，又没有其他的rules，就创建一个验证规则；
            * 2、若设置了required属性，又存在其他的rules，则只rules中不存在required属性时，才添加验证required的规则
            *     也就是说rules中的required，优先级大于required
            */
            if (getRequired) {
                if (!rules || rules.length === 0) { 
                    rules = [{ required: getRequired, validator }];
                } else {
                    const requiredIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required'));

                    if (requiredIndex === -1) {
                        rules.push({ required: getRequired, validator });
                    }
                }
            }

            const requiredRuleIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'));

            if (requiredRuleIndex !== -1) {
                const rule = rules[requiredRuleIndex];
                const { isShow } = getShow();
                if (!isShow) { rule.required = false; }
                if (component) {
                    if (!Reflect.has(rule, 'type')) {
                        rule.type = component === 'InputNumber' ? 'number' : 'string';
                    }

                    rule.message = rule.message || defaultMsg;

                    if (component.includes('Input') || component.includes('Textarea')) {
                        rule.whitespace = true;
                    }
                    const valueFormat = unref(getComponentsProps)?.valueFormat;
                    setComponentRuleType(rule, component, valueFormat);
                }
            }

            // Maximum input length rule check
            const characterInx = rules.findIndex((val) => val.max);
            if (characterInx !== -1 && !rules[characterInx].validator) {
                rules[characterInx].message = rules[characterInx].message || `字符数应小于${rules[characterInx].max}位`;
            }

            return rules;
        }

        // 处理自定义组件逻辑
        function renderComponent() {
            const {
                renderComponentContent,
                component,
                field,
                changeEvent = 'change',
                valueField,
            }:any = props.schema;

            const isCheck = component && ['Switch', 'Checkbox'].includes(component);

            const eventKey = `on${upperFirst(changeEvent)}`;
            
            // 组件事件处理
            const on = {
                [eventKey]: (...args: Nullable<Recordable>[]) => {
                    const [e] = args;
                    if (propsData[eventKey]) {
                    propsData[eventKey](...args);
                    }
                    const target = e ? e.target : null;
                    const value = target ? (isCheck ? target.checked : target.value) : e;

                    props.itemChange(field, value);
                },
            };

            const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

            const { autoSetPlaceHolder, size } = props.formProps;

            const getPopupContainer = component == 'Select' ? { getPopupContainer: (trigger: Element) => {
                // 判断是否存在对话框？ 如果存在对话框以对话框为准
                const modalBody = document.querySelector('.ant-modal-body');
                if(modalBody) return modalBody;

                return trigger.parentNode;
            } } : {};
            
            // 这里设置的是使用render函数定义组件的情况设置默认的宽度值
            const setDefWidthObj = getCompDefWidth(component); // 获取默认设置配置
            const setCompWidth = { style: setDefWidthObj };

            // 组件传递props数据
            const propsData: Recordable = {
                allowClear: true,
                size,
                ...getPopupContainer,
                ...setCompWidth,
                ...unref(getComponentsProps),
                disabled: unref(getDisable),
            };

            const isCreatePlaceholder = !propsData.disabled && autoSetPlaceHolder;

            // 处理非数组的placeholder提示语； 涉及到RangePicker是数组的情况
            if (isCreatePlaceholder && component !== 'RangePicker' && component) {
                propsData.placeholder = unref(getComponentsProps)?.placeholder || createPlaceholderMessage(component);
            }
            propsData.codeField = field;

            propsData.formValues = unref(getRecordData);

            const bindValue: Recordable = {
                [valueField || (isCheck ? 'checked' : 'value')]: props.bindModel[field],
            };

            // 组件传递的Attts数据
            const compAttr: Recordable = {
                ...propsData,
                ...on,
                ...bindValue,
            };
            
            // 非自定义组件内容情况
            if (!renderComponentContent) { return <Comp {...compAttr} />; }

            // 自定义组件内容时： 处理成插槽形式
            const compSlot = isFunction(renderComponentContent) ? { ...renderComponentContent(unref(getRecordData)) }
            : { default: () => renderComponentContent };

            return <Comp {...compAttr}>{compSlot}</Comp>;
        }

        // 处理label提示信息
        function renderLabelHelpMessage() {
            const { label, helpMessage, helpComponentProps, subLabel, subLabelProps, custSubLabelReander }:any = props.schema;
            // 处理表单项子标题
            const sublabelCustComp = isFunction(custSubLabelReander) ?
            <span>{label}{ custSubLabelReander(unref(getRecordData)) }</span>
            : (
                <span>
                    {label} <span class="text-secondary" {...subLabelProps}>{subLabel}</span>
                </span>
            );

            const renderLabel = subLabel || isFunction(custSubLabelReander) ? sublabelCustComp : ( label );


            const getHelpMessage = isFunction(helpMessage) ? helpMessage(unref(getRecordData)) : helpMessage;

            if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
                return renderLabel;
            }

            return (
                <span>
                    <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps}>
                        { renderLabel }&nbsp;
                        <QuestionCircleOutlined />
                    </BasicHelp>
                </span>
            );
        }

        // 处理表单每项渲染逻辑
        function renderItem() {
            const { itemProps, slot, render, field, suffix, component, componentBottomContent } = props.schema;
            const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
            const { colon } = props.formProps;

            if (component === 'Divider') {
                if(render){
                    const getContent = () => render(unref(getRecordData));
                    return (<Col span={24}>{getContent()}</Col>);
                }else{
                    return (
                        <Col span={24}>
                            <Divider  {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</Divider>
                        </Col>
                    );
                }
            } else {
                const getContent = () => {
                    return slot
                    ? getSlot(slots, slot, unref(getRecordData))
                    : render
                    ? render(unref(getRecordData))
                    : renderComponent();
                };

                const showSuffix = !!suffix;
                const getSuffix = isFunction(suffix) ? suffix(unref(getRecordData)) : suffix;
                return (
                    <Form.Item
                        name={field}
                        colon={colon}
                        class={{ 'suffix-item': showSuffix }}
                        {...(itemProps as Recordable)}
                        label={renderLabelHelpMessage()}
                        rules={handleRules()}
                        labelCol={labelCol}
                        wrapperCol={wrapperCol}
                    >
                        <div style="display:flex">
                            <div style="flex:1;">{getContent()}</div>
                            {showSuffix && <span class="suffix">{getSuffix}</span>}
                        </div>
                        { 
                            componentBottomContent && <div style="display:flex">{ isFunction(componentBottomContent) ? componentBottomContent(unref(getRecordData)) : componentBottomContent }</div> 
                        }
                    </Form.Item>
                );
            }
        }

        // console.log(
        //     'formItem:', 
        //     'recrod(事件抛出记录): ', unref(getRecordData), 
        //     '组件props传递值：',unref(getComponentsProps), 
        //     '是否禁用：' , unref(getDisable),
        // );

        return () => {
            const { colProps = {}, colSlot, renderColContent, component }:any = props.schema;
            if (!componentMap.has(component)) {
                return null;
            }

            const { baseColProps = {} } = props.formProps;
            const realColProps:any = { ...baseColProps, ...colProps };
            const { isIfShow, isShow } = getShow();
            const values = unref(getRecordData);

            const getContent = () => {
            return colSlot
                ? getSlot(slots, colSlot, values)
                : renderColContent
                ? renderColContent(values)
                : renderItem();
            };

            return (isIfShow && (<Col {...realColProps} v-show={isShow}>{getContent()}</Col>));
        };
    },
});
</script>
